// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © tradeforopp

//@version=5
indicator("The Vet [TFO]", "The Vet [TFO]", true, max_lines_count = 500, max_boxes_count = 500, max_labels_count = 500)

get_table_pos(string pos) =>
    result = switch pos
        "Bottom Center" => position.bottom_center
        "Bottom Left" => position.bottom_left
        "Bottom Right" => position.bottom_right
        "Middle Center" => position.middle_center
        "Middle Left" => position.middle_left
        "Middle Right" => position.middle_right
        "Top Center" => position.top_center
        "Top Left" => position.top_left
        "Top Right" => position.top_right
    result

get_size(string size) =>
    result = switch size
        'Tiny' => size.tiny
        'Small' => size.small
        'Normal' => size.normal
        'Large' => size.large
        'Huge' => size.huge
        'Auto' => size.auto
    result

get_line_type(string style) =>
    result = switch style
        'Solid' => line.style_solid
        'Dotted' => line.style_dotted
        'Dashed' => line.style_dashed
    result

var g_PJ = "Projections"
show_pjs = input.bool(true, "Show Range Projections", group = g_PJ)
show_pj_hits = input.bool(true, "Show Projection Hits", inline = "PJH", group = g_PJ)
pj_hit_color = input.color(color.blue, "", inline = "PJH", group = g_PJ)
show_pj_table = input.bool(true, "Show Projection Table", group = g_PJ)
session = input.session("0930-1030", "Session", inline = "SR", group = g_PJ)
day_anchor = input.string("Tuesday", "Day Anchor", options = ['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'], group = g_PJ)
res_tf = input.timeframe("5", "Resolution Timeframe", tooltip = "The timeframe that will be used to build the DR profile", group = g_PJ)
projections = input.string("1,2,3,4,6,8", "Projection Levels", tooltip = "Enter projection levels (numbers) separated by commas", group = g_PJ)
range_lw = input.int(2, "Range Line Width", group = g_PJ)
pj_tbl_pos = get_table_pos(input.string("Top Right", "Table Position", options = ['Bottom Center', 'Bottom Left', 'Bottom Right', 'Middle Center', 'Middle Left', 'Middle Right', 'Top Center', 'Top Left', 'Top Right'], group = g_PJ))
rch_color = input.color(color.new(color.gray, 70), "Table - Reached Level Color", group = g_PJ)

tbl_FTP = input.bool(true, "Table - First Touch Probability", group = g_PJ)
tbl_OTP = input.bool(true, "Table - Other Side Touch Probability", group = g_PJ)
tbl_NLT = input.bool(true, "Table - Next Level Touch Probability", group = g_PJ)
tbl_ADR = input.bool(true, "Table - Within ADR", group = g_PJ)
tbl_AWR = input.bool(true, "Table - Within AWR", group = g_PJ)

var g_SESSIONS = "Distributions"
show_dist_boxes = input.bool(true, "Show Distribution Boxes", inline = "DBL", group = g_SESSIONS)
show_dist_labels = input.bool(true, "Show Labels", inline = "DBL", group = g_SESSIONS)
show_dist_table = input.bool(true, "Show Distribution Table", inline = "DTBL", group = g_SESSIONS)
sample_size = input.bool(false, "Sample Sizes", inline = "DTBL", group = g_SESSIONS)
dist_show_pcts = input.bool(false, "Display Values As Percents", group = g_SESSIONS)
filter_dow = input.string("Total", "Range Average Display", options = ['Total','Day of Week'], group = g_SESSIONS)

s1 = input.session("0400-0800", "", inline = "S1", group = g_SESSIONS)
s2 = input.session("0800-1200", "", inline = "S2", group = g_SESSIONS)
s3 = input.session("1200-1600", "", inline = "S3", group = g_SESSIONS)
s4 = input.session("1600-0400", "", inline = "S4", group = g_SESSIONS)

c1 = input.color(color.new(color.blue,  70), "", inline = "S1", group = g_SESSIONS)
c2 = input.color(color.new(color.red,   70), "", inline = "S2", group = g_SESSIONS)
c3 = input.color(color.new(color.teal,  70), "", inline = "S3", group = g_SESSIONS)
c4 = input.color(color.new(color.black, 70), "", inline = "S4", group = g_SESSIONS)

dist_tbl_pos = get_table_pos(input.string("Bottom Right", "Table Position", options = ['Bottom Center', 'Bottom Left', 'Bottom Right', 'Middle Center', 'Middle Left', 'Middle Right', 'Top Center', 'Top Left', 'Top Right'], group = g_SESSIONS))

var g_ADR = "Average Range"
show_ar_table = input.bool(true, "Show Average Range Table", group = g_ADR)
show_adr = input.bool(true, "ADR", inline = "ADR", group = g_ADR)
show_awr = input.bool(true, "AWR", inline = "AWR", group = g_ADR)
adr_len = input.int(10, "", inline = "ADR", group = g_ADR)
awr_len = input.int(8, "", inline = "AWR", group = g_ADR)
adr_color = input.color(color.new(color.teal, 70), "", inline = "ADR", group = g_ADR)
awr_color = input.color(color.new(color.red, 70), "", inline = "AWR", group = g_ADR)
ar_lw = input.int(2, "Average Range Line Width", group = g_ADR)
ar_tbl_pos = get_table_pos(input.string("Middle Right", "Table Position", options = ['Bottom Center', 'Bottom Left', 'Bottom Right', 'Middle Center', 'Middle Left', 'Middle Right', 'Top Center', 'Top Left', 'Top Right'], group = g_ADR))

var g_STYLE = "Style"
dr_style = get_line_type(input.string("Solid", "DR", inline = "DR", options = ['Solid','Dashed','Dotted'], group = g_STYLE))
idr_style = get_line_type(input.string("Dashed", "IDR", inline = "IDR", options = ['Solid','Dashed','Dotted'], group = g_STYLE))
pj_style = get_line_type(input.string("Dotted", "Projections", inline = "PJ", options = ['Solid','Dashed','Dotted'], group = g_STYLE))
tbl_bg = input.color(color.white, "Table Background", group = g_STYLE)
tbl_bd = input.color(color.black, "Table Borders", group = g_STYLE)
tbl_txt = input.color(color.black, "Table Text", group = g_STYLE)

dr_color = input.color(color.blue, "", inline = "DR", group = g_STYLE)
idr_color = input.color(color.blue, "", inline = "IDR", group = g_STYLE)
pj_color = input.color(color.black, "", inline = "PJ", group = g_STYLE)

var day_check = switch day_anchor
    'Monday' => dayofweek.monday
    'Tuesday' => dayofweek.tuesday
    'Wednesday' => dayofweek.wednesday
    'Thursday' => dayofweek.thursday
    'Friday' => dayofweek.friday
    'Saturday' => dayofweek.saturday
    'Sunday' => dayofweek.sunday

valid_day = dayofweek == day_check
t = not na(time("", session, "America/New_York"))

[ltf_o, ltf_h, ltf_l, ltf_c] = request.security_lower_tf(syminfo.tickerid, res_tf, [open, high, low, close])

valid_t = valid_day and t
var pjs = str.split(projections, ",")

type adr
    string tf
    float value = na
    float cur_hi = na
    float cur_lo = na
    float cur_op = na
    float cur_range = na
    bool hit_up = false
    bool hit_dn = false
    int hit_up_ct = 0
    int hit_dn_ct = 0
    int sessions = 0
    float[] ranges

type day_range
    box idr = na
    line dr_hi = na
    line dr_lo = na
    line idr_hi = na
    line idr_lo = na
    int time_start = na
    line[] dr_hi_plines
    line[] dr_lo_plines
    label[] dr_hi_plabels
    label[] dr_lo_plabels
    bool[] hit_dr_hi
    bool[] hit_dr_lo

type ses_stats
    float[] ranges
    float[] range_pcts
    int[] day
    box[] boxes
    label[] labels_avg
    label[] labels_act
    string session
    color obj_color
    int count = 0
    float cur_hi = na
    float cur_lo = na
    float cur_op = na

var s1_stats = ses_stats.new(array.new_float(), array.new_float(), array.new_int(), array.new_box(), array.new_label(), array.new_label(), s1, c1)
var s2_stats = ses_stats.new(array.new_float(), array.new_float(), array.new_int(), array.new_box(), array.new_label(), array.new_label(), s2, c2)
var s3_stats = ses_stats.new(array.new_float(), array.new_float(), array.new_int(), array.new_box(), array.new_label(), array.new_label(), s3, c3)
var s4_stats = ses_stats.new(array.new_float(), array.new_float(), array.new_int(), array.new_box(), array.new_label(), array.new_label(), s4, c4)

t1 = not na(time("", s1, "America/New_York"))
t2 = not na(time("", s2, "America/New_York"))
t3 = not na(time("", s3, "America/New_York"))
t4 = not na(time("", s4, "America/New_York"))

var DAY = array.new<day_range>()

var d_adr = adr.new(tf = "D", ranges = array.new_float())
var w_adr = adr.new(tf = "W", ranges = array.new_float())

get_dow_range(ses_stats S, int dow) =>
    sum = 0.0
    count = 0
    for [i, s] in S.day
        if s == dow
            sum += S.range_pcts.get(i)
            count += 1
    result = sum / count * open / 100

get_dow_stats(ses_stats S, int dow, bool D) =>
    sum = 0.0
    count = 0
    for [i, s] in S.day
        if D ? s == dow : true
            sum += (dist_show_pcts ? S.range_pcts.get(i) : S.ranges.get(i))
            count += 1
    result = str.tostring(math.round(sum / count, 2))+(dist_show_pcts?"%":"")
    result += sample_size ? (" ["+str.tostring(count)+"]") : ""

tbl_stats_dow(ses_stats S, table T, int R) =>
    T.cell(0, 1 + R, text_color = tbl_txt, text = S.session)
    
    T.cell(1, 1 + R, text_color = tbl_txt, text = get_dow_stats(S, dayofweek.monday, true))
    T.cell(2, 1 + R, text_color = tbl_txt, text = get_dow_stats(S, dayofweek.tuesday, true))
    T.cell(3, 1 + R, text_color = tbl_txt, text = get_dow_stats(S, dayofweek.wednesday, true))
    T.cell(4, 1 + R, text_color = tbl_txt, text = get_dow_stats(S, dayofweek.thursday, true))
    T.cell(5, 1 + R, text_color = tbl_txt, text = get_dow_stats(S, dayofweek.friday, true))

tbl_stats_all(ses_stats S, table T, int R) =>
    T.cell(6, 1 + R, text_color = tbl_txt, text = get_dow_stats(S, na, false))

method update_ses_stats(ses_stats S, bool t) =>
    if t and not t[1]
        S.count += 1
        S.cur_hi := high
        S.cur_lo := low
        S.cur_op := open
        S.day.unshift(dayofweek)
        S.ranges.unshift(0)
        S.range_pcts.unshift(0)
        avg = 0.0
        if dist_show_pcts
            avg := S.range_pcts.avg()
        else
            avg := S.ranges.avg()

        range_avg = (filter_dow != "Total") ? get_dow_range(S, dayofweek) : S.ranges.avg()

        S.boxes.unshift(box.new(time, open+range_avg/2, time, open-range_avg/2, xloc = xloc.bar_time, bgcolor = show_dist_boxes ? S.obj_color : na, border_color = show_dist_boxes ? S.obj_color : na))
        if show_dist_labels
            if dist_show_pcts
                S.labels_avg.unshift(label.new(time, open+range_avg/2, str.format("Avg: {0,number}%", math.round(avg, 2)), xloc = xloc.bar_time, textcolor = color.new(S.obj_color, 0), color = #ffffff00))
                S.labels_act.unshift(label.new(time, open-range_avg/2, str.format("Actual: {0,number}%", math.round((S.cur_hi - S.cur_lo) / S.cur_lo * 100, 2)), xloc = xloc.bar_time, textcolor = color.new(S.obj_color, 0), color = #ffffff00, style = label.style_label_up))
            else
                S.labels_avg.unshift(label.new(time, open+range_avg/2, "Avg: "+str.tostring(math.round(avg, 2)), xloc = xloc.bar_time, textcolor = color.new(S.obj_color, 0), color = #ffffff00))
                S.labels_act.unshift(label.new(time, open-range_avg/2, "Actual: "+str.tostring(S.cur_hi - S.cur_lo), xloc = xloc.bar_time, textcolor = color.new(S.obj_color, 0), color = #ffffff00, style = label.style_label_up))
    else if t
        S.boxes.get(0).set_right(time)
        S.cur_hi := math.max(S.cur_hi, high)
        S.cur_lo := math.min(S.cur_lo, low)
        if show_dist_labels
            S.labels_avg.get(0).set_x(math.floor(math.avg(S.boxes.get(0).get_left(), S.boxes.get(0).get_right())))
            S.labels_act.get(0).set_x(math.floor(math.avg(S.boxes.get(0).get_left(), S.boxes.get(0).get_right())))
            if dist_show_pcts
                S.labels_act.get(0).set_text(str.format("Actual: {0,number}%", math.round((S.cur_hi - S.cur_lo) / S.cur_lo * 100, 2)))
            else
                S.labels_act.get(0).set_text("Actual: "+str.tostring(S.cur_hi - S.cur_lo))

        h_dif = S.cur_hi - S.boxes.get(0).get_top()
        l_dif = S.boxes.get(0).get_bottom() - S.cur_lo

        if h_dif > 0 and (S.cur_hi - S.cur_lo < S.ranges.avg())
            temp_top = S.boxes.get(0).get_top()
            temp_bot = S.boxes.get(0).get_bottom()
            S.boxes.get(0).set_top(temp_top + h_dif)
            S.boxes.get(0).set_bottom(temp_bot + h_dif)
            if show_dist_labels
                S.labels_avg.get(0).set_y(S.labels_avg.get(0).get_y() + h_dif)
                S.labels_act.get(0).set_y(S.labels_act.get(0).get_y() + h_dif)
        if l_dif > 0 and (S.cur_hi - S.cur_lo < S.ranges.avg())
            temp_top = S.boxes.get(0).get_top()
            temp_bot = S.boxes.get(0).get_bottom()
            S.boxes.get(0).set_top(temp_top - l_dif)
            S.boxes.get(0).set_bottom(temp_bot - l_dif)
            if show_dist_labels
                S.labels_avg.get(0).set_y(S.labels_avg.get(0).get_y() - l_dif)
                S.labels_act.get(0).set_y(S.labels_act.get(0).get_y() - l_dif)

    if not t and t[1]
        S.ranges.set(0, S.cur_hi - S.cur_lo)
        S.range_pcts.set(0, (S.cur_hi - S.cur_lo) / S.cur_op * 100)

method update_adr(adr A, bool is_adr) =>
    if timeframe.change(A.tf)
        if not na(A.cur_hi)
            A.ranges.unshift(A.cur_hi - A.cur_lo)
            if A.ranges.size() > (is_adr ? adr_len : awr_len)
                A.ranges.pop()
            A.value := A.ranges.avg()
        A.cur_hi := high
        A.cur_lo := low
        A.cur_op := open
        A.hit_up := false
        A.hit_dn := false
        if not na(A.value)
            A.sessions += 1
    else
        A.cur_hi := math.max(A.cur_hi, high)
        A.cur_lo := math.min(A.cur_lo, low)
        if not na(A.value)
            A.cur_range := (A.cur_hi - A.cur_lo) / A.value * 100
            if high > A.cur_op + A.value / 2 and not A.hit_up
                A.hit_up_ct += 1
                A.hit_up := true
            if low  < A.cur_op - A.value / 2 and not A.hit_dn
                A.hit_dn_ct += 1
                A.hit_dn := true

d_adr.update_adr(true)
w_adr.update_adr(false)

plot(not show_adr ? na : d_adr.cur_op + d_adr.value / 2, color = color.new(adr_color, 0), linewidth = ar_lw)
plot(not show_adr ? na : d_adr.cur_op - d_adr.value / 2, color = color.new(adr_color, 0), linewidth = ar_lw)

plot(not show_awr ? na : w_adr.cur_op + w_adr.value / 2, color = color.new(awr_color, 0), linewidth = ar_lw)
plot(not show_awr ? na : w_adr.cur_op - w_adr.value / 2, color = color.new(awr_color, 0), linewidth = ar_lw)

s1_stats.update_ses_stats(t1)
s2_stats.update_ses_stats(t2)
s3_stats.update_ses_stats(t3)
s4_stats.update_ses_stats(t4)

if show_pjs
    if valid_t and not valid_t[1] and ltf_h.size() > 0
        DAY.unshift(day_range.new(dr_hi_plines = array.new_line(), dr_lo_plines = array.new_line(), dr_hi_plabels = array.new_label(), dr_lo_plabels = array.new_label(),
             hit_dr_hi = array.new_bool(), hit_dr_lo = array.new_bool()))
        c = DAY.get(0)
        c.time_start := time

        dr_hi = ltf_h.max()
        dr_lo = ltf_l.min()

        c.dr_hi := line.new(time, dr_hi, time+timeframe.in_seconds("W")*1000, dr_hi, xloc = xloc.bar_time, style = dr_style, color = dr_color, width = range_lw)
        c.dr_lo := line.new(time, dr_lo, time+timeframe.in_seconds("W")*1000, dr_lo, xloc = xloc.bar_time, style = dr_style, color = dr_color, width = range_lw)

        idr_hi = math.max(ltf_o.max(), ltf_c.max())
        idr_lo = math.min(ltf_o.min(), ltf_c.min())

        c.idr_hi := line.new(time, idr_hi, time+timeframe.in_seconds("W")*1000, idr_hi, xloc = xloc.bar_time, style = idr_style, color = idr_color, width = range_lw)
        c.idr_lo := line.new(time, idr_lo, time+timeframe.in_seconds("W")*1000, idr_lo, xloc = xloc.bar_time, style = idr_style, color = idr_color, width = range_lw)

        dif = dr_hi - dr_lo

        for p in pjs
            int_p = str.tonumber(p)

            c.dr_hi_plines.push(line.new(time, idr_hi + dif * int_p, time+timeframe.in_seconds("W")*1000, idr_hi + dif * int_p, xloc = xloc.bar_time, color = pj_color, style = pj_style, width = range_lw))
            c.dr_lo_plines.push(line.new(time, idr_lo - dif * int_p, time+timeframe.in_seconds("W")*1000, idr_lo - dif * int_p, xloc = xloc.bar_time, color = pj_color, style = pj_style, width = range_lw))

            c.dr_hi_plabels.push(label.new(time, dr_hi + dif * int_p, "+"+p, xloc.bar_time, style = label.style_label_right, color = #ffffff00, textcolor = chart.fg_color))
            c.dr_lo_plabels.push(label.new(time, dr_lo - dif * int_p, "-"+p, xloc.bar_time, style = label.style_label_right, color = #ffffff00, textcolor = chart.fg_color))

            c.hit_dr_hi.push(false)
            c.hit_dr_lo.push(false)
    else if valid_t and DAY.size() > 0
        c = DAY.get(0)
        c.dr_hi.set_y1(math.max(c.dr_hi.get_y1(), ltf_h.max()))
        c.dr_hi.set_y2(math.max(c.dr_hi.get_y1(), ltf_h.max()))

        c.dr_lo.set_y1(math.min(c.dr_lo.get_y1(), ltf_l.min()))
        c.dr_lo.set_y2(math.min(c.dr_lo.get_y1(), ltf_l.min()))

        c.idr_hi.set_y1(math.max(c.idr_hi.get_y1(), math.max(ltf_o.max(), ltf_c.max())))
        c.idr_hi.set_y2(math.max(c.idr_hi.get_y1(), math.max(ltf_o.max(), ltf_c.max())))

        c.idr_lo.set_y1(math.min(c.idr_lo.get_y1(), math.min(ltf_o.min(), ltf_c.min())))
        c.idr_lo.set_y2(math.min(c.idr_lo.get_y1(), math.min(ltf_o.min(), ltf_c.min())))

        dif = c.idr_hi.get_y1() - c.idr_lo.get_y1()
        hi = c.idr_hi.get_y1()
        lo = c.idr_lo.get_y1()
            
        for [i, p] in pjs
            int_p = str.tonumber(p)

            ph_line = c.dr_hi_plines.get(i)
            ph_line.set_y1(hi + dif * int_p)
            ph_line.set_y2(hi + dif * int_p)
                
            pl_line = c.dr_lo_plines.get(i)
            pl_line.set_y1(lo - dif * int_p)
            pl_line.set_y2(lo - dif * int_p)

            ph_label = c.dr_hi_plabels.get(i)
            ph_label.set_y(hi + dif * int_p)

            pl_label = c.dr_lo_plabels.get(i)
            pl_label.set_y(lo - dif * int_p)
    else if not valid_t and DAY.size() > 0
        c = DAY.get(0)
        for [i, p] in pjs
            ph_line = c.dr_hi_plines.get(i)
            if high > ph_line.get_y1() and not c.hit_dr_hi.get(i)
                c.hit_dr_hi.set(i, true)
                if show_pj_hits
                    label.new(bar_index, high, color = pj_hit_color)
                
            pl_line = c.dr_lo_plines.get(i)
            if low < pl_line.get_y1() and not c.hit_dr_lo.get(i)
                c.hit_dr_lo.set(i, true)
                if show_pj_hits
                    label.new(bar_index, low, color = pj_hit_color, style = label.style_label_up)

get_cell_color(bool hit) =>
    hit ? rch_color : na

get_adr_pct(adr A) =>
    x = math.floor((A.cur_hi-A.cur_lo) / A.value * 1000)/10
    result = str.tostring(A.cur_hi-A.cur_lo)+" ("+str.tostring(x)+"%)"

get_adr_hit(adr A) =>
    x = math.round(A.hit_up_ct / A.sessions * 100, 1)
    result = str.tostring(x)+"%/"
    x := math.round(A.hit_dn_ct / A.sessions * 100, 1)
    result := result + str.tostring(x)+"%"

get_FTP(n, bull) =>
    int count = 0
    bool hit = false
    if DAY.size() > 0
        for [i, c] in DAY
            if bull ? (c.hit_dr_hi.get(n)) : (c.hit_dr_lo.get(n))
                count += 1
                if i == 0
                    hit := true
    result = count / DAY.size() * 100
    str_result = str.tostring(math.round(result, 1))+"%"
    [str_result, hit]

get_OTP(n) =>
    int count = 0
    bool hit = false
    if DAY.size() > 0
        for [i, c] in DAY
            if c.hit_dr_hi.get(n) and c.hit_dr_lo.get(n)
                count += 1
                if i == 0
                    hit := true
    result = count / DAY.size() * 100
    str_result = str.tostring(math.round(result, 1))+"%"
    [str_result, hit]

get_NLT(n, bull) =>
    int count = 0
    int sample = 0
    bool hit = false
    if DAY.size() > 0
        cur = DAY.get(0)
        if bull ? (cur.hit_dr_hi.get(n)) : (cur.hit_dr_lo.get(n))
            hit := true
        for [i, c] in DAY
            if i != 0
                if n > 0
                    if bull ? (c.hit_dr_hi.get(n - 1)) : (c.hit_dr_lo.get(n - 1))
                        sample += 1
                        if bull ? (c.hit_dr_hi.get(n)) : (c.hit_dr_lo.get(n))
                            count += 1
                else
                    if bull ? (c.hit_dr_hi.get(n)) : (c.hit_dr_lo.get(n))
                        count += 1
    result = count / (sample != 0 ? sample : DAY.size()) * 100
    str_result = str.tostring(math.round(result, 1))+"%"
    [str_result, hit]

get_WADR(n, bull) =>
    string sample = "NO"
    bool hit = false
    if DAY.size() > 0
        c = DAY.get(0)
        adr_hi = d_adr.cur_op + d_adr.value / 2
        adr_lo = d_adr.cur_op - d_adr.value / 2

        if bull 
            if c.dr_hi_plines.get(n).get_y1() <= adr_hi and c.dr_hi_plines.get(n).get_y1() >= adr_lo
                sample := "YES"
                hit := true
        else
            if c.dr_lo_plines.get(n).get_y1() <= adr_hi and c.dr_lo_plines.get(n).get_y1() >= adr_lo
                sample := "YES"
                hit := true
    [sample, hit]

get_WAWR(n, bull) =>
    string sample = "NO"
    bool hit = false
    if DAY.size() > 0
        c = DAY.get(0)
        adr_hi = w_adr.cur_op + w_adr.value / 2
        adr_lo = w_adr.cur_op - w_adr.value / 2

        if bull 
            if c.dr_hi_plines.get(n).get_y1() <= adr_hi and c.dr_hi_plines.get(n).get_y1() >= adr_lo
                sample := "YES"
                hit := true
        else
            if c.dr_lo_plines.get(n).get_y1() <= adr_hi and c.dr_lo_plines.get(n).get_y1() >= adr_lo
                sample := "YES"
                hit := true
    [sample, hit]

if barstate.islast
    if show_pj_table and show_pjs
        var tbl = table.new(pj_tbl_pos, 50, 50, tbl_bg, tbl_bd, 2, tbl_bd, 1)

        num_weeks = DAY.size()

        tbl.cell(0, 0, text_color = tbl_txt, text = "Level\n(Weeks: "+str.tostring(num_weeks)+")")
        if tbl_FTP
            tbl.cell(1, 0, text_color = tbl_txt, text = "First\nTouch")
        if tbl_OTP
            tbl.cell(2, 0, text_color = tbl_txt, text = "Other\nSide\nTouch")
        if tbl_NLT
            tbl.cell(3, 0, text_color = tbl_txt, text = "Next\nLevel\nTouch")
        if tbl_ADR
            tbl.cell(4, 0, text_color = tbl_txt, text = "Within\nADR")
        if tbl_AWR
            tbl.cell(5, 0, text_color = tbl_txt, text = "Within\nAWR")

        for [i, p] in pjs
            [ftp_str_up, ftp_color_up] = get_FTP(i, true)
            [ltp_str_up, ltp_color_up] = get_NLT(i, true)
            [adr_str, adr_str_color] = get_WADR(i, true)
            [awr_str, awr_str_color] = get_WAWR(i, true)
            [otp_str, otp_color] = get_OTP(i)

            tbl.cell(0, pjs.size() - i, text_color = tbl_txt, text = "+"+p)
            if tbl_FTP
                tbl.cell(1, pjs.size() - i, text_color = tbl_txt, text = ftp_str_up, bgcolor = get_cell_color(ftp_color_up))
            if tbl_OTP
                tbl.cell(2, pjs.size() - i, text_color = tbl_txt, text = otp_str, bgcolor = get_cell_color(otp_color))
            if tbl_NLT
                tbl.cell(3, pjs.size() - i, text_color = tbl_txt, text = ltp_str_up, bgcolor = get_cell_color(ltp_color_up))
            if tbl_ADR
                tbl.cell(4, pjs.size() - i, text_color = tbl_txt, text = adr_str, bgcolor = get_cell_color(adr_str_color))
            if tbl_AWR
                tbl.cell(5, pjs.size() - i, text_color = tbl_txt, text = awr_str, bgcolor = get_cell_color(awr_str_color))

        for [i, p] in pjs
            [ftp_str_dn, ftp_color_dn] = get_FTP(i, false)
            [ltp_str_dn, ltp_color_dn] = get_NLT(i, false)
            [adr_str, adr_str_color] = get_WADR(i, false)
            [awr_str, awr_str_color] = get_WAWR(i, false)
            [otp_str, otp_color] = get_OTP(i)

            tbl.cell(0, i + 1 + pjs.size(), text_color = tbl_txt, text = "-"+p)
            if tbl_FTP
                tbl.cell(1, i + 1 + pjs.size(), text_color = tbl_txt, text = ftp_str_dn, bgcolor = get_cell_color(ftp_color_dn))
            if tbl_OTP
                tbl.cell(2, i + 1 + pjs.size(), text_color = tbl_txt, text = otp_str, bgcolor = get_cell_color(otp_color))
            if tbl_NLT
                tbl.cell(3, i + 1 + pjs.size(), text_color = tbl_txt, text = ltp_str_dn, bgcolor = get_cell_color(ltp_color_dn))
            if tbl_ADR
                tbl.cell(4, i + 1 + pjs.size(), text_color = tbl_txt, text = adr_str, bgcolor = get_cell_color(adr_str_color))
            if tbl_AWR
                tbl.cell(5, i + 1 + pjs.size(), text_color = tbl_txt, text = awr_str, bgcolor = get_cell_color(awr_str_color))

    if show_ar_table
        var tbl = table.new(ar_tbl_pos, 50, 50, tbl_bg, tbl_bd, 2, tbl_bd, 1)
        if d_adr.ranges.size() == adr_len
            tbl.cell(0, 0, text_color = tbl_txt, text = "ADR"+str.tostring(adr_len), bgcolor = adr_color)
            tbl.cell(0, 1, text_color = tbl_txt, text = "Current", bgcolor = adr_color)
            tbl.cell(0, 2, text_color = tbl_txt, text = "Hit +/- ADR", bgcolor = adr_color)

            tbl.cell(1, 0, text_color = tbl_txt, text = str.tostring(math.round(d_adr.value, 1)), bgcolor = adr_color)
            tbl.cell(1, 1, text_color = tbl_txt, text = get_adr_pct(d_adr), bgcolor = adr_color)
            tbl.cell(1, 2, text_color = tbl_txt, text = get_adr_hit(d_adr), bgcolor = adr_color)
        if w_adr.ranges.size() == awr_len
            tbl.cell(0, 3, text_color = tbl_txt, text = "AWR"+str.tostring(awr_len), bgcolor = awr_color)
            tbl.cell(0, 4, text_color = tbl_txt, text = "Current", bgcolor = awr_color)
            tbl.cell(0, 5, text_color = tbl_txt, text = "Hit +/- AWR", bgcolor = awr_color)
            
            tbl.cell(1, 3, text_color = tbl_txt, text = str.tostring(math.round(w_adr.value, 1)), bgcolor = awr_color)
            tbl.cell(1, 4, text_color = tbl_txt, text = get_adr_pct(w_adr), bgcolor = awr_color)
            tbl.cell(1, 5, text_color = tbl_txt, text = get_adr_hit(w_adr), bgcolor = awr_color)

    if show_dist_table
        var tbl = table.new(dist_tbl_pos, 50, 50, tbl_bg, tbl_bd, 2, tbl_bd, 1)
        tbl.cell(0, 0, text_color = tbl_txt, text = "Session")
        
        tbl.cell(1, 0, text_color = tbl_txt, text = "MON")
        tbl.cell(2, 0, text_color = tbl_txt, text = "TUE")
        tbl.cell(3, 0, text_color = tbl_txt, text = "WED")
        tbl.cell(4, 0, text_color = tbl_txt, text = "THU")
        tbl.cell(5, 0, text_color = tbl_txt, text = "FRI")
        tbl.cell(6, 0, text_color = tbl_txt, text = "Total")

        tbl_stats_dow(s1_stats, tbl, 1)
        tbl_stats_dow(s2_stats, tbl, 2)
        tbl_stats_dow(s3_stats, tbl, 3)
        tbl_stats_dow(s4_stats, tbl, 4)

        tbl_stats_all(s1_stats, tbl, 1)
        tbl_stats_all(s2_stats, tbl, 2)
        tbl_stats_all(s3_stats, tbl, 3)
        tbl_stats_all(s4_stats, tbl, 4)